﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Media.Animation;
using System.Data;
using System.Diagnostics;
using Microsoft.Win32;
using System.Threading.Tasks;
using System.Threading;
using System.Globalization;

namespace PracaInz_v0
{
	public partial class MainWindow : Window
	{
        //dla pojedynczego drzewa(dane)
        TreeD SingleTree;
        //dla pojedynczego drzewa (widok)
        DrawTree SingleTreeView;
        //dla kilku drzew (dane i widok)
        CompleatTree[] Forest;
        DataTable AllVect;
        //ustawienia i bindowanie
        TreeViewSettings SettingsI = new TreeViewSettings();
        TreeViewSettings SettingsS = new TreeViewSettings();

        TaskScheduler taskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
        Stopwatch SW;

		public MainWindow()
		{
			this.InitializeComponent();
            CultureInfo ci = new System.Globalization.CultureInfo("en-us");
            Thread.CurrentThread.CurrentCulture = ci;

            Zoom();  // wlacza sterowanie podgladem drzew

            MainGridI.DataContext = SettingsI;
            MainGridS.DataContext = SettingsS;
            MainGridC.DataContext = SettingsS;
		}

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            if (App.ARGS != null)
                this.Hide();
        }

        #region createForest

        private void CreateTreesButton_Click(object sender, RoutedEventArgs e)
        {
            SW = Stopwatch.StartNew();

            TreesDataListBox.Items.Clear();
            TreesViewsListBox.Items.Clear();
            CreateTreesButton.IsEnabled = false;
            OpenFile.IsEnabled = false;
            GBMethodI.IsEnabled = false;
            CTI_Click();
        }

        void CTI_Click()
        {
            int mNV = 0;
            int mNVP = 0;
            int mL = 0;
            double mVN = 0;
            int RandVectCount = 0;
            int TreesCount = 0;
            
            Forest = null;

            try
            {
                RandVectCount = int.Parse(textBoxRandVect.Text);    //ilosc losowanych wektorow
                TreesCount = int.Parse(textBoxNumTrees.Text);       //ilosc generowanych drzew

                Forest = new CompleatTree[TreesCount];

                for(int i = 0; i < Forest.Length; i++) // deklaracja drzew
                {
                    Forest[i] = new CompleatTree();
                    Forest[i].TreeData = new TreeD();
                }

                mNV = int.Parse(textBoxMNVI.Text);         //minimalna ilosc wektorow potomka
                mNVP = int.Parse(textBoxMNVPI.Text);       //minimalna ilosc potonkow dla rodzica
                mL = int.Parse(textBoxMLI.Text);           //max poziom 
                mVN = double.Parse(textBoxMVNI.Text);      //minimalna wariancja wezla            
            }
            catch (Exception ex)
            {
                MessageBox.Show("Zle podane dane \n" + ex.ToString());
                CreateTreesButton.IsEnabled = true;
                OpenFile.IsEnabled = true;
                GBMethodI.IsEnabled = true;
                return;
            }
            // tworzenie katalogu na dane
            TreeD.CreateRootDir("Induction");

            var MainTask = GenerateTreesData(RandVectCount, mNV, mNVP, mL, mVN);

            MainTask
                .ContinueWith(delegate
                {
                    this.Dispatcher.Invoke((Action)(() =>
                    {
                        CreateTreesButton.IsEnabled = true;
                        OpenFile.IsEnabled = true;
                        GBMethodI.IsEnabled = true;
                        ClassifyIButton.IsEnabled = true;

                        SW.Stop();
                        TimeSpan ts = SW.Elapsed;
                        BottomBarInfo.Instance.ExecutionTimeInfo = string.Format("{0}:{1}", Math.Floor(ts.TotalMinutes), ts.ToString("ss\\.fff"));
                    }));
                });            
        }

        async Task GenerateTreesData(int vect, int mnv, int mnvp, int ml, double mvn)
        {
            List<Task> list = new List<Task>();

            for (int i = 0; i < Forest.Length; i++)
            {
                int counter = i; //zapamietuje na potrzeby continuewith
                Task ts = Task.Run(() => 
                {
                    Forest[counter].Vectors = CompleatTree.RandVect(AllVect, vect);
                    Forest[counter].TreeData.AssignAJ(Forest[counter].Vectors);
                    Forest[counter].TreeData.mNV = mnv;
                    Forest[counter].TreeData.mNVP = mnvp;
                    Forest[counter].TreeData.mL = ml;
                    Forest[counter].TreeData.mVN = mvn;
                });
                await ts;

                //po wylosowaniu wektorow obliczamy drzewo i rysyjemy
                await ts.ContinueWith(delegate
                {
                    ForestMethod(counter);      //obliczanie
                    this.Dispatcher.Invoke((Action)(() =>
                    {
                        TreesDataListBox.Items.Add(new ForestButtonData(counter, Forest[counter].TreeData.TreeDT, Forest[counter].TreeData.SymulationDT, FindResource("MetroButtonStyle"), SettingsI, Forest[counter].TreeData.Quality));                      
                    }));            
                });
                await ts.ContinueWith(delegate
                {
                    ForestDrawTree(counter);    //rysowanaie
                    this.Dispatcher.Invoke((Action)(() =>
                    {
                        TreesViewsListBox.Items.Add(new ForestButtonView(counter, Forest[counter].TreeView, FindResource("MetroButtonStyle"), SettingsI));
                    }));   
                });

                list.Add(ts);
            }
            await Task.WhenAll(list); 
        }

        private void ForestDrawTree(int l)          //implementacja rysowania
        {            
            Task.Factory.StartNew(() =>
            {
                Forest[l].TreeView = new DrawTree(SettingsI, Forest[l].Vectors, Forest[l].TreeData.TreeDT, Forest[l].TreeData.NodeVectSource);
            },
            CancellationToken.None,
            TaskCreationOptions.None,
            taskScheduler);
        }

        private void ForestMethod(int l)            //implementacja obliczania
        {
            if (SettingsI.RbMinVar)
            {
                Forest[l].TreeData.Tree("TreeMinV", Forest[l].TreeData.minVar, Forest[l].TreeData.verificationNode, l);
            }
            else if (SettingsI.RbMLP)
            {
                Forest[l].TreeData.Tree("TreeMLP", Forest[l].TreeData.neuronMLP, Forest[l].TreeData.verificationNode, l);
            }
            else if (SettingsI.RbMinVar2a)
            {
                Forest[l].TreeData.Tree("MinV2A", Forest[l].TreeData.minVar2A, Forest[l].TreeData.verificationNode, l);
            }
        }

        private void OpenFileI_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                OpenFileDialog open = new OpenFileDialog();
                open.RestoreDirectory = true;
                open.Filter = "Text documents (.txt)|*.txt"; // Filter files by extension

                Nullable<bool> result = open.ShowDialog();

                if (result == true)
                {
                    AllVect = LoadData.loadFile(open.FileName.ToString(), textBoxXI, textBoxYI);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Zle podane dane \n" + ex.ToString());
                return;
            }
        }

        async private void ClassifyVectI_Click(object sender, RoutedEventArgs e)
        {
            DataTable tmp = new DataTable();
            try
            {
                OpenFileDialog ofd = new OpenFileDialog();
                ofd.RestoreDirectory = true;

                Nullable<bool> result = ofd.ShowDialog();

                if (result == true)
                {
                    tmp = await Task.FromResult<DataTable>(LoadData.loadFileVect2(ofd.FileName.ToString()));

                    bool mode = CCCheckBox.IsChecked == true ? true : false;
                    await Task.Run(() =>
                    {
                        new ExternalVectors().ClassifyNewVect_Forest(Forest, tmp, mode);
                    });
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Data are wrong \n" + ex.ToString());
            }
        }

        #endregion createForest

        #region createSingleTree

        private void LoadVectC_Click(object sender, RoutedEventArgs e)
        {
            if (SingleTree == null)
                SingleTree = new TreeD();

            SingleTree.AJTree = null;
            SingleTree.AJTrain = null;

            Boolean b = true;
            try
            {
                SingleTree.mNV = int.Parse(textBoxMNVC.Text);         //minimalna ilosc wektorow potomka
                SingleTree.mNVP = int.Parse(textBoxMNVPC.Text);       //minimalna ilosc potonkow dla rodzica
                SingleTree.mL = int.Parse(textBoxMLC.Text);           //max poziom 
                SingleTree.mVN = double.Parse(textBoxMVNC.Text);      //minimalna wariancja wezla            
            }
            catch (Exception ex)
            {
                b = false;
                MessageBox.Show("Zle podane dane \n" + ex.ToString());
            }

            if (b)
            {
                SingleTree.loadFile(textBoxXC, textBoxYC);
                CreateTreeButton.IsEnabled = true;
            }
        }       

        async private void CreateTreeButton_Click(object sender, RoutedEventArgs e)
        {
            CreateTreeButton.IsEnabled = false;
            await Task.Run(() =>
            {
                SW = Stopwatch.StartNew();

                if (SettingsS.RbMinVar)
                {
                    SingleTree.Tree(SettingsS, "TreeMinV", SingleTree.minVar, SingleTree.verificationNode);
                }
                else if (SettingsS.RbMLP)
                {
                    SingleTree.Tree(SettingsS, "TreeMLP", SingleTree.neuronMLP, SingleTree.verificationNode);
                }
                else if (SettingsS.RbMinVar2a)
                {
                    SingleTree.Tree(SettingsS, "MinV2A", SingleTree.minVar2A, SingleTree.verificationNode);
                }

                SW.Stop();
                TimeSpan ts = SW.Elapsed;
                BottomBarInfo.Instance.ExecutionTimeInfo = string.Format("{0}:{1}", Math.Floor(ts.TotalMinutes), ts.ToString("ss\\.fff"));
            });
        }

        private void LoadTreeC_Click(object sender, RoutedEventArgs e)
        {
            if (SingleTree == null)
                SingleTree = new TreeD();
            try
            {
                SingleTree.loadFileTree();
            }
            catch { }
        }

        private void LoadSetC_Click(object sender, RoutedEventArgs e)
        {
            if (SingleTree == null)
                SingleTree = new TreeD();
            try
            {
                SingleTree.loadFileS();
            }
            catch { }
        }

        async private void SymulationC_Click(object sender, RoutedEventArgs e)
        {
            await Task.Run(() =>
            {
                try
                {                
                    SingleTree.AJT = SingleTree.symulation();
                    SingleTree.AssignSymulationDT();
                    SettingsS.DataViewGridS = SingleTree.SymulationDT.DefaultView;
                    SingleTree.saveTreeAll(SettingsS);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.ToString());
                }
            });

        }

        async private void QualityC_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                await Task.Run(() =>
                    {
                        string q = Math.Round(TreeD.quality(SingleTree.AJT),5).ToString();

                        this.Dispatcher.Invoke(() =>
                            {
                                textBoxQualityC.Text = q;
                            });
                    });
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        private void textBoxNumTrees_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (CreateTreesButton != null)
            {
                int i;
                TextBox tb = sender as TextBox;
                int.TryParse(tb.Text, out i);
                if (i >= 2 && i <= 10)
                    CreateTreesButton.IsEnabled = true;
                else
                    CreateTreesButton.IsEnabled = false;
            }
        }

        #endregion createSingleTree

        #region showSingleTree

        delegate DataTable LoadingFileDelegate(string source);

        private void LoadTreeS_Click(object sender, RoutedEventArgs e)
        {
            SingleTreeViewData.Instance.Tree = LoadFilesToShowSingleTree(LoadData.loadFile).Result;
            SingleTreeViewData.Instance.TreeLoaded = true;
        }        

        private void LoadVectS_Click(object sender, RoutedEventArgs e)
        {
            SingleTreeViewData.Instance.Vectors = LoadFilesToShowSingleTree(LoadData.loadFileVect).Result;
            SingleTreeViewData.Instance.VectorsLoaded = true;
        }

        private void LoadNodesSources_Click(object sender, RoutedEventArgs e)
        {
            SingleTreeViewData.Instance.NodeVectSource = LoadFilesToShowSingleTree(LoadData.LoadNodeVectSource).Result;
            SingleTreeViewData.Instance.NodeVectSourceLoaded = true;
        }

        async private Task<DataTable> LoadFilesToShowSingleTree(LoadingFileDelegate loadFile)
        {
            DataTable tmp = new DataTable();
            try
            {
                OpenFileDialog ofd = new OpenFileDialog();
                ofd.RestoreDirectory = true;
                ofd.Filter = "Text documents (.txt)|*.txt"; // Filter files by extension

                Nullable<bool> result = ofd.ShowDialog();

                if (result == true)
                {
                    tmp = await Task.FromResult<DataTable>(loadFile(ofd.FileName.ToString()));
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Data are wrong \n" + ex.ToString());
            }
            return tmp;
        }

        async private void ShowTreeS_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                await Task.Factory.StartNew(() =>
                {
                    SingleTreeView = new DrawTree(SettingsS, 
                        SingleTreeViewData.Instance.Vectors, 
                        SingleTreeViewData.Instance.Tree, 
                        SingleTreeViewData.Instance.NodeVectSource);
                    SettingsS.TreeView = SingleTreeView.TreeCanvas;
                    ClassifyButton.IsEnabled = true;
                },
                CancellationToken.None,
                TaskCreationOptions.None,
                taskScheduler);
            }
            catch (Exception ex)
            {
                MessageBox.Show("Data are wrong \n" + ex.ToString());
            }
        }


        async private void ClassifyVectS_Click(object sender, RoutedEventArgs e)
        {
            DataTable tmp = new DataTable();
            try
            {
                OpenFileDialog ofd = new OpenFileDialog();
                ofd.RestoreDirectory = true;

                Nullable<bool> result = ofd.ShowDialog();

                if (result == true)
                {
                    tmp = await Task.FromResult<DataTable>(LoadData.loadFileVect2(ofd.FileName.ToString()));

                    bool mode = CCCheckBox.IsChecked == true ? true : false;
                    await Task.Run(() =>
                        {
                            new ExternalVectors().ClassifyNewVect_SingleTree(SingleTreeView, tmp, mode);
                        });
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Data are wrong \n" + ex.ToString());
            }
        }

        #endregion showSingleTree
     
        #region UI

        Point? lastCenterPositionOnTarget;
        Point? lastMousePositionOnTarget;
        Point? lastDragPoint;

        //obsluguja zdarzenia wyswietlania w roznych zakladkach
        ScrollViewer SV;
        Grid G;

        //ikony
        bool Icon1_click = false;
        bool Icon2_click = false;
        bool Icon3_click = false;
        bool IconRefresh_click = false;
        bool IsMenuShowS_click = false;
        bool IsMenuShowS = true;
        bool IsMenuShowI_click = false;
        bool IsMenuShowI = true;

        void RestartApp()
        {
            Process.Start(Application.ResourceAssembly.Location);
            Application.Current.Shutdown();
        }

        void clear_points()
        {
            lastCenterPositionOnTarget = null;
            lastMousePositionOnTarget = null;
            lastDragPoint = null;
        }

        private void rectangleRefresh_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (IconRefresh_click)
            {
                RestartApp();
            }
            IconRefresh_click = false;
        }

        private void rectangleRefresh_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            IconRefresh_click = true;
        }

        private void rectangle1_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (Icon1_click)
            {
                ContentTabControl.SelectedIndex = 0;
                clear_points();
            }
            Icon1_click = false;
        }

        private void rectangle1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            Icon1_click = true;
        }

        private void rectangle2_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (Icon2_click)
            {
                ContentTabControl.SelectedIndex = 1;
                clear_points();
            }
            Icon2_click = false;
        }

        private void rectangle2_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            Icon2_click = true;
        }

        private void rectangle3_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (Icon3_click)
            {
                ContentTabControl.SelectedIndex = 2;
                clear_points();
            }
            Icon3_click = false;
        }

        private void rectangle3_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            Icon3_click = true;
        }

        private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (Window.WindowState != System.Windows.WindowState.Maximized)
            {
                ShowHideButtonS.Margin = new Thickness(ShowHideButtonS.Margin.Left, ((Window.Height - 195) / 2), 0, 0);
                ShowHideArrowS.Margin = new Thickness(ShowHideArrowS.Margin.Left, ((Window.Height - 130) / 2), 0, 0);

                ShowHideButtonI.Margin = new Thickness(ShowHideButtonI.Margin.Left, ((Window.Height - 217) / 2), 0, 0);
                ShowHideArrowI.Margin = new Thickness(ShowHideArrowI.Margin.Left, ((Window.Height - 152) / 2), 0, 0);
            }
            else 
            {
                ShowHideButtonS.Margin = new Thickness(ShowHideButtonS.Margin.Left, ((System.Windows.SystemParameters.PrimaryScreenHeight - 225) / 2), 0, 0);
                ShowHideArrowS.Margin = new Thickness(ShowHideArrowS.Margin.Left, ((System.Windows.SystemParameters.PrimaryScreenHeight - 160) / 2), 0, 0);

                ShowHideButtonI.Margin = new Thickness(ShowHideButtonI.Margin.Left, ((System.Windows.SystemParameters.PrimaryScreenHeight - 247) / 2), 0, 0);
                ShowHideArrowI.Margin = new Thickness(ShowHideArrowI.Margin.Left, ((System.Windows.SystemParameters.PrimaryScreenHeight - 182) / 2), 0, 0);               
            }
        }

        private void ShowHideButtonS_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (IsMenuShowS_click)
            {
                if (IsMenuShowS)
                {
                    Storyboard HideMenu = (Storyboard)this.Resources["HideMenuS"];
                    HideMenu.Begin();
                    IsMenuShowS = false;
                }
                else
                {
                    Storyboard ShowMenu = (Storyboard)this.Resources["ShowMenuS"];
                    ShowMenu.Begin();
                    IsMenuShowS = true;
                }
            }
            IsMenuShowS_click = false;
        }

        private void ShowHideButtonI_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (IsMenuShowI_click)
            {
                if (IsMenuShowI)
                {
                    Storyboard HideMenu = (Storyboard)this.Resources["HideMenuI"];
                    HideMenu.Begin();
                    IsMenuShowI = false;
                }
                else
                {
                    Storyboard ShowMenu = (Storyboard)this.Resources["ShowMenuI"];
                    ShowMenu.Begin();
                    IsMenuShowI = true;
                }
            }
            IsMenuShowI_click = false;
        }

        private void ShowHideButtonS_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            IsMenuShowS_click = true;
        }

        private void ShowHideButtonI_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            IsMenuShowI_click = true;
        }

        private void canvasS_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            grid1.Width = canvasS.ActualWidth - 20;

            if(!RegFormulaGridS.IsVisible)
                grid1.Height = canvasS.ActualHeight - 10;
            else
                grid1.Height = canvasS.ActualHeight - 50;

            RegFormulaGridS.Width = canvasS.ActualWidth;
        }

        private void canvasI_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            grid2.Width = canvasI.ActualWidth - 20;

            if (!RegFormulaGridI.IsVisible)
                grid2.Height = canvasI.ActualHeight - 10;
            else
                grid2.Height = canvasI.ActualHeight - 50;

            RegFormulaGridI.Width = canvasI.ActualWidth;
        }

        void Zoom()
        {
            //zakladka pojedyncze drzewo S
            scrollViewerS.ScrollChanged += OnScrollViewerScrollChanged;
            scrollViewerS.MouseLeftButtonUp += OnMouseButtonUp;
            scrollViewerS.PreviewMouseRightButtonUp += OnMouseButtonUp;
            scrollViewerS.PreviewMouseWheel += OnPreviewMouseWheelS;
            scrollViewerS.PreviewMouseRightButtonDown += OnMouseRightButtonDown;
            scrollViewerS.MouseMove += OnMouseMove;
            sliderS.ValueChanged += OnSliderValueChangedS;

            //zakladka indukcja I
            scrollViewerI.ScrollChanged += OnScrollViewerScrollChanged;
            scrollViewerI.MouseLeftButtonUp += OnMouseButtonUp;
            scrollViewerI.PreviewMouseRightButtonUp += OnMouseButtonUp;
            scrollViewerI.PreviewMouseWheel += OnPreviewMouseWheelI;
            scrollViewerI.PreviewMouseRightButtonDown += OnMouseRightButtonDown;
            scrollViewerI.MouseMove += OnMouseMove;
            sliderI.ValueChanged += OnSliderValueChangedI;

        }

        void OnMouseMove(object sender, MouseEventArgs e)
        {
            if (lastDragPoint.HasValue)
            {
                SV = (ScrollViewer)sender;

                Point posNow = e.GetPosition(SV);

                double dX = posNow.X - lastDragPoint.Value.X;
                double dY = posNow.Y - lastDragPoint.Value.Y;

                lastDragPoint = posNow;

                SV.ScrollToHorizontalOffset(SV.HorizontalOffset - dX);
                SV.ScrollToVerticalOffset(SV.VerticalOffset - dY);
            }
        }

        void OnMouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            SV = (ScrollViewer)sender;

            var mousePos = e.GetPosition(SV);
            if (mousePos.X <= SV.ViewportWidth && mousePos.Y < SV.ViewportHeight) //make sure we still can use the scrollbars
            {
                SV.Cursor = Cursors.SizeAll;
                lastDragPoint = mousePos;
                Mouse.Capture(SV);
            }
        }

        void OnPreviewMouseWheelS(object sender, MouseWheelEventArgs e)
        {
            lastMousePositionOnTarget = Mouse.GetPosition(gridS);

            if (e.Delta > 0)
            {
                sliderS.Value += 1;
            }
            if (e.Delta < 0)
            {
                sliderS.Value -= 1;
            }

            e.Handled = true;
        }

        void OnPreviewMouseWheelI(object sender, MouseWheelEventArgs e)
        {
            lastMousePositionOnTarget = Mouse.GetPosition(gridI);

            if (e.Delta > 0)
            {
                sliderI.Value += 1;
            }
            if (e.Delta < 0)
            {
                sliderI.Value -= 1;
            }

            e.Handled = true;
        }

        void OnMouseButtonUp(object sender, MouseButtonEventArgs e)
        {
            SV = (ScrollViewer)sender;
            SV.Cursor = Cursors.Arrow;
            SV.ReleaseMouseCapture();
            lastDragPoint = null;
        }

        void OnSliderValueChangedS(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            scaleTransformS.ScaleX = e.NewValue;
            scaleTransformS.ScaleY = e.NewValue;

            var centerOfViewport = new Point(scrollViewerS.ViewportWidth / 2, scrollViewerS.ViewportHeight / 2);
            lastCenterPositionOnTarget = scrollViewerS.TranslatePoint(centerOfViewport, gridS);
        }

        void OnSliderValueChangedI(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            scaleTransformI.ScaleX = e.NewValue;
            scaleTransformI.ScaleY = e.NewValue;

            var centerOfViewport = new Point(scrollViewerI.ViewportWidth / 2, scrollViewerI.ViewportHeight / 2);
            lastCenterPositionOnTarget = scrollViewerI.TranslatePoint(centerOfViewport, gridI);
        }

        void OnScrollViewerScrollChanged(object sender, ScrollChangedEventArgs e)
        {
            SV = (ScrollViewer)sender;

            if (ContentTabControl.SelectedIndex == 1)
                G = gridS;
            else if (ContentTabControl.SelectedIndex == 0)
                G = gridI;

            if (e.ExtentHeightChange != 0 || e.ExtentWidthChange != 0)
            {
                Point? targetBefore = null;
                Point? targetNow = null;

                if (!lastMousePositionOnTarget.HasValue)
                {
                    if (lastCenterPositionOnTarget.HasValue)
                    {
                        var centerOfViewport = new Point(SV.ViewportWidth / 2, SV.ViewportHeight / 2);
                        Point centerOfTargetNow = SV.TranslatePoint(centerOfViewport, G);

                        targetBefore = lastCenterPositionOnTarget;
                        targetNow = centerOfTargetNow;
                    }
                }
                else
                {
                    targetBefore = lastMousePositionOnTarget;
                    targetNow = Mouse.GetPosition(G);

                    lastMousePositionOnTarget = null;
                }

                if (targetBefore.HasValue)
                {
                    double dXInTargetPixels = targetNow.Value.X - targetBefore.Value.X;
                    double dYInTargetPixels = targetNow.Value.Y - targetBefore.Value.Y;

                    double multiplicatorX = e.ExtentWidth / G.ActualWidth;
                    double multiplicatorY = e.ExtentHeight / G.ActualHeight;

                    double newOffsetX = SV.HorizontalOffset - dXInTargetPixels * multiplicatorX;
                    double newOffsetY = SV.VerticalOffset - dYInTargetPixels * multiplicatorY;

                    if (double.IsNaN(newOffsetX) || double.IsNaN(newOffsetY))
                    {
                        return;
                    }

                    SV.ScrollToHorizontalOffset(newOffsetX);
                    SV.ScrollToVerticalOffset(newOffsetY);
                }
            }
        }

        private void canvasMS_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            gridMS.Height = canvasMS.ActualHeight - 10;
        }

        private void canvasMI_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            gridMI.Height = canvasMI.ActualHeight - 10;
        }     

        private void RegFormulaGridI_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if(RegFormulaGridI.IsVisible)
                grid2.Height = canvasI.ActualHeight - 50;
            else
                grid2.Height = canvasI.ActualHeight - 10;
        }

        private void RegFormulaGridS_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (RegFormulaGridS.IsVisible)
                grid1.Height = canvasS.ActualHeight - 50;
            else
                grid1.Height = canvasS.ActualHeight - 10;
        }

        #endregion
    }
}